package com.ipan.jfinal.controller;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Workbook;

import com.ipan.jfinal.excel.DefaultXlsHander;
import com.ipan.jfinal.render.MyCaptchaRender;
import com.ipan.jfinal.render.MyRenderFactory;
import com.ipan.jfinal.service.BaseService;
import com.ipan.jfinal.service.ServiceException;
import com.ipan.jfinal.ui.ruoyi.PageDomain;
import com.ipan.kits.base.Platforms;
import com.ipan.kits.id.IdUtil;
import com.ipan.kits.io.FileUtil;
import com.ipan.kits.reflect.ClassLoaderUtil;
import com.ipan.kits.reflect.ReflectionUtil;
import com.ipan.kits.text.MoreStringUtil;
import com.ipan.kits.web.ServletUtil;
import com.ipan.poi.excel.XlsObjectFactory;
import com.ipan.poi.excel.config.ExportConfiguration;
import com.ipan.poi.excel.config.XlsEntity;
import com.ipan.poi.excel.exporter.XlsExportable;
import com.ipan.poi.excel.hander.XlsHander;
import com.ipan.poi.excel.importer.XlsResult;
import com.ipan.poi.excel.service.XlsService;
import com.jfinal.core.Controller;
import com.jfinal.core.JFinal;
import com.jfinal.core.NotAction;
import com.jfinal.kit.Kv;
import com.jfinal.log.Log;
import com.jfinal.plugin.activerecord.Model;
import com.jfinal.plugin.activerecord.Page;
import com.jfinal.render.RenderManager;

/**
 * 基础控制器，方便获取登录信息
 * 
 * @author iPan
 * @version 2021-11-18
 */
@SuppressWarnings("rawtypes")
public abstract class BaseController<T extends Model> extends Controller {
	public static final String CONTROLLER = "Controller";
	public static final String EXCEL_RECORD_CODE = "excel_record_code";
//	public static final String INPUT = "index.html"; // 这块每个项目不太通用，废弃！
//	public static final String LIST = "list.html";
//	public static final String VIEW = "view.html";
	
	protected Class<T> modelClass;
	protected T model = null;
	protected int defaultPageSize = 10;
	/** 默认导入excel处理器 */
	protected XlsHander defaultXlsHander = null;
	/** 默认导出excel工具 */
	protected static final XlsExportable DEFAULT_XLS_EXPORTER = XlsObjectFactory.createXlsExporter();
	protected Log log = Log.getLog(this.getClass());
	
	@SuppressWarnings("unchecked")
	public BaseController() {
//		if (ReflectionKit.isProxyClass(this.getClass())) {
//			this.modelClass = (Class<T>) ReflectionKit.getSuperClassGenricType(this.getClass().getSuperclass());
//		} else {
			this.modelClass = (Class<T>) ReflectionUtil.getSuperClassGenricType(this.getClass());
//		}
		model = newModel();
	}
	
	protected T newModel() {
		T obj = null;
		try {
			obj = this.modelClass.newInstance();
		} catch (Exception e) {
			log.error("newModel error", e);
			throw new ServiceException("创建实体类出错！", e);
		}
		return obj;
	}
	
	/**
	 * 根据字符串生成模板输出string
	 */
	protected String renderToStrByStrTemplate(String content, Map data) {
		return RenderManager.me().getEngine().getTemplateByString(content).renderToString(data);
	}
	protected String renderToStrByStrTemplate(String content) {
		Map data = getRequest().getParameterMap();
		return RenderManager.me().getEngine().getTemplateByString(content).renderToString(data);
	}
	
	protected void renderImage(File file) {
		MyRenderFactory mf = (MyRenderFactory) RenderManager.me().getRenderFactory();
		render(mf.getImageRender(file));
	}
	protected void renderImage(String filePath) {
		MyRenderFactory mf = (MyRenderFactory) RenderManager.me().getRenderFactory();
		render(mf.getImageRender(new File(filePath)));
	}
	/**
	 * JWT版验证码，key放在http head里面；
	 */
	protected void renderHeadCaptcha() {
		MyRenderFactory mf = (MyRenderFactory) RenderManager.me().getRenderFactory();
		render(mf.getHeadCaptchaRender());
	}
	
	protected void renderTextFile(String text, String downloadFileName) {
		MyRenderFactory mf = (MyRenderFactory) RenderManager.me().getRenderFactory();
		render(mf.getTextFileRender(text, downloadFileName));
	}
	protected void renderTextFile(String text, String downloadFileName, String contentType, String encode) {
		MyRenderFactory mf = (MyRenderFactory) RenderManager.me().getRenderFactory();
		render(mf.getTextFileRender(text, downloadFileName, contentType, encode));
	}
	
	protected XlsHander getDefaultXlsHander() {
		if (defaultXlsHander == null) {
			this.defaultXlsHander = new DefaultXlsHander((XlsService<Object>) this.getMainService());
		}
		return defaultXlsHander;
	}
	/**
	 * 导入XLS共通调用
	 */
	protected XlsResult doImportXls(File file, String fileName, String className, XlsHander hander) throws IOException {
		return XlsObjectFactory.createXlsImporter().importExcel(file, fileName, className, hander);
	}
	protected XlsResult doImportXls(File file, String fileName, String className) throws IOException {
		return XlsObjectFactory.createXlsImporter().importExcel(file, fileName, className, getDefaultXlsHander());
	}
	
	/**
	 * 导出XLS共通调用
	 */
	protected void doExportXls(List<?> entityList, Workbook wb) throws IOException { // 大数据传入SXSSFWorkbook对象
		XlsEntity defEntity = ExportConfiguration.getInstance().getEntity(modelClass.getName());
		if (defEntity == null) {
			this.renderError(Integer.parseInt(ResultDefineManager.me().getFail().getLeft()), "excel导出配置错误！");
			return ;
		}
		// 输出
		OutputStream out = null;
		out = getResponse().getOutputStream();
		// 设置头部
		ServletUtil.setFileDownloadHeader(getResponse(), defEntity.getFileName() + ".xlsx");
		getXlsExporter().exportByEntity(defEntity, entityList, out, XlsObjectFactory.createCellStyleCreater(IndexedColors.PALE_BLUE.getIndex()), 0, "sheet1", wb);
		renderNull();
	}
	protected void doExportXls(List<?> entityList) throws IOException {
		doExportXls(entityList, null);
	}
	
	/**
	 * 导出XLS共通调用
	 * 改为写入到服务端，客户端收到响应后重新提交下载请求；
	 * 请求下载的时候，下载成功后就删除临时文件；
	 */
	protected String doExportXls2(List<?> entityList, Workbook wb) throws IOException { // 大数据传入SXSSFWorkbook对象
		XlsEntity defEntity = ExportConfiguration.getInstance().getEntity(modelClass.getName());
		if (defEntity == null) {
			this.renderError(Integer.parseInt(ResultDefineManager.me().getFail().getLeft()), "excel导出配置错误！");
			return null;
		}
		// 改为写入临时目录，前端重新提交下载请求，下载完成后删除文件；
		String tempPath = JFinal.me().getConstants().getBaseDownloadPath() +"temp/";
		FileUtil.makesureDirExists(tempPath); // 生成目录
		FileOutputStream fout = null;
		String outFileName = IdUtil.nanoId() + ".xlsx";
		try {
			fout = new FileOutputStream(new File(tempPath + outFileName));
			getXlsExporter().exportByEntity(defEntity, entityList, fout, XlsObjectFactory.createCellStyleCreater(IndexedColors.PALE_BLUE.getIndex()), 0, "sheet1", wb);
			return outFileName;
		} catch (Exception e) {
			log.error("导出excel" + defEntity.getFileName() + "失败", e);
		} finally {
			if (fout != null) {
				try {
					fout.close();
				} catch (Exception e) {
				}
			}
		}
		return null;
	}
	protected String doExportXls2(List<?> entityList) throws IOException {
		return doExportXls2(entityList, null);
	}
	
	// 子类可以覆盖该方法
	protected XlsExportable getXlsExporter() {
		return DEFAULT_XLS_EXPORTER;
	}
	
	protected T requestToModel() {
		return this.getBean(this.modelClass, null, false);
	}
	
	protected <X> X requestToModel(Class<X> obj) {
		return this.getBean(obj, null, false);
	}
	
	/**
	 * 根据约定规则获取当前业务的Service
	 */
	protected <Y extends BaseService> Y getMainService() { // 不可为public！ActionMapping编译会出错！
		String cname = this.getClass().getName();
		if (!cname.endsWith(CONTROLLER)) {
			throw new RuntimeException("错误的控制器命名：" + cname);
		}
		cname = cname.substring(0, cname.lastIndexOf(CONTROLLER)) + BaseService.SERVICE;
		try {
			Class classService = ClassLoaderUtil.loadClass(cname, this.getClass());
			BaseService defaultService = (BaseService) classService.getField("me").get(null);
			return (Y) defaultService;
		} catch (Exception e) {
			log.error("getMainService error", e);
			throw new RuntimeException("获取mainService失败：" + cname, e);
		}
	}
	
	@NotAction
	public boolean validateHeadCaptcha(String paraName) {
		return MyCaptchaRender.validate(this, getPara(paraName));
	}
	
	protected String getParaTrim(String name) {
		return MoreStringUtil.trim(getPara(name));
	}
	
	protected String getParaTrim(int index) {
		return MoreStringUtil.trim(getPara(index));
	}
//	public void index() {
//		render(INPUT);
//	}
//	
//	public void list() {
//		render(LIST); 
//	}
	
	/**
	 * 保存或更新
	 * 若不够用请自己在子类覆盖！
	 */
//	public void save() { // 具备新增、更新的功能
//		try {
//			Model model = requestToModel();
//			if (StrKit.isBlank(this.getPara("id"))) { // 新增
//				insertInit(model);
//				getMainService().save(model);
//			} else { // 更新
//				updateInit(model);
//				getMainService().update(model);
//			} 
//			renderJson(retOk());
//		} catch (Exception e) {
//			log.error("save error", e);
//			renderJson(retFail());
//		}
//	}
	
//	public void delete() {
//		try {
//			getMainService().deleteById(this.getParaToInt("id"));
//			renderJson(retOk("删除成功"));
//		} catch (Exception e) {
//			log.error("delete error", e);
//			renderJson(retFail());
//		}
//	}
	
//	public void view() {
//		Object md = getMainService().findById(this.getParaToInt("id"));
//		setAttr("md", md);
//		render(VIEW);
//	}
//	
//	public void edit() {
//		Object md = getMainService().findById(this.getParaToInt("id"));
//		setAttr("md", md);
//		render(INPUT);
//	}
	
//	protected void insertInit(Model model) {
//		Date sysDate = new Date();
//		Method m = null;
//		try {
//			m = model.getClass().getMethod("getCreateTime");
//			if (m != null) {
//				Object val = ReflectionUtil.invokeGetter(model, "createTime");
//				if (val == null) {
//					ReflectionUtil.invokeSetter(model, "createTime", sysDate);
//				}
//			}
//		} catch (Exception e) {
//		}
//		
//		try {
//			m = model.getClass().getMethod("getUpdateTime");
//			if (m != null) {
//				Object val = ReflectionUtil.invokeGetter(model, "updateTime");
//				if (val == null) {
//					ReflectionUtil.invokeSetter(model, "updateTime", sysDate);
//				}
//			}
//		} catch (Exception e) {
//		}
//		UserInfo userinfo = UserInfoManager.getUserInfo(getRequest(), null); // 注意：多会话场景，需要自己重载！
//		if (userinfo != null && StringUtils.isNotBlank(userinfo.getUserName())) {
//			String userName = userinfo.getUserName();
//			try {
//				m = model.getClass().getMethod("getCreateBy");
//				if (m != null) {
//					Object val = ReflectionUtil.invokeGetter(model, "createBy");
//					if (val == null) {
//						ReflectionUtil.invokeSetter(model, "createBy", userName);
//					}
//				}
//			} catch (Exception e) {
//			}
//			try {
//				m = model.getClass().getMethod("getUpdateBy");
//				if (m != null) {
//					Object val = ReflectionUtil.invokeGetter(model, "updateBy");
//					if (val == null) {
//						ReflectionUtil.invokeSetter(model, "updateBy", userName);
//					}
//				}
//			} catch (Exception e) {
//			}
//		}
//	}
//	
//	protected void updateInit(Model model) {
//		Date sysDate = new Date();
//		Method m = null;
//		try {
//			m = model.getClass().getMethod("getUpdateTime");
//			if (m != null) {
//				Object val = ReflectionUtil.invokeGetter(model, "updateTime");
//				if (val == null) {
//					ReflectionUtil.invokeSetter(model, "updateTime", sysDate);
//				}
//			}
//		} catch (Exception e) {
//		}
//		UserInfo userinfo = UserInfoManager.getUserInfo(getRequest(), null); // 注意：多会话场景，需要自己重载！
//		if (userinfo != null && StringUtils.isNotBlank(userinfo.getUserName())) {
//			String userName = userinfo.getUserName();
//			try {
//				m = model.getClass().getMethod("getUpdateBy");
//				if (m != null) {
//					Object val = ReflectionUtil.invokeGetter(model, "updateBy");
//					if (val == null) {
//						ReflectionUtil.invokeSetter(model, "updateBy", userName);
//					}
//				}
//			} catch (Exception e) {
//			}
//		}
//	}
	
	// --- 前端UI封装 --- //
	protected Kv pageToData(Page<?> page) {
		if (page == null) {
			return null;
		}
		ResultDefineManager rdm = ResultDefineManager.me();
		Kv result = Kv.by(rdm.getCodeKey(), rdm.getSuccess().getLeft())
			.set(rdm.getMsgKey(), rdm.getSuccess().getRight())
			.set(rdm.getTotalKey(), page.getTotalRow())
			.set(rdm.getRowsKey(), page.getList());
		return result;
	}
	protected Kv listToData(List<?> list) {
		if (list == null) {
			return null;
		}
		ResultDefineManager rdm = ResultDefineManager.me();
		Kv result = Kv.by(rdm.getCodeKey(), rdm.getSuccess().getLeft())
			.set(rdm.getMsgKey(), rdm.getSuccess().getRight())
			.set(rdm.getTotalKey(), list.size())
			.set(rdm.getRowsKey(), list);
		return result;
	}
	protected PageDomain buildPageDomain() { // 前端提交的分页参数
		ResultDefineManager rdm = ResultDefineManager.me();
		PageDomain pageDomain = new PageDomain();
        pageDomain.setPageNum(getInt(rdm.getPageNum()));
        pageDomain.setPageSize(getInt(rdm.getPageSize()));
        pageDomain.setOrderByColumn(getPara(rdm.getOrderByColumn()));
        pageDomain.setIsAsc(getPara(rdm.getIsAsc()));
        return pageDomain;
	}
	
	protected Kv retOk() {
		return retOk("操作成功", null);
	}
	protected Kv retOk(String msg) {
		return retOk(msg, null);
	}
	protected Kv retOkData(Object data) {
		return retOk("操作成功", data);
	}
	protected Kv retOk(String msg, Object data) { // 若retObj是Map，前端不支持包装成result对象，那么，需要自行处理！
		ResultDefineManager rdm = ResultDefineManager.me();
		Kv ret = Kv.by(rdm.getCodeKey(), ResultDefineManager.me().getSuccess().getLeft())
				.set(rdm.getMsgKey(), (msg != null && msg.length() > 0) ? msg : ResultDefineManager.me().getSuccess().getRight()); // 0是成功，前端layui默认0是成功；
		if (data != null) {
			ret.set("data", data);
		}
		return ret;
	}
	protected Kv retFail() {
		return retFail("操作失败");
	}
	protected Kv retFail(String msg) {
		ResultDefineManager rdm = ResultDefineManager.me();
		return Kv.by(rdm.getCodeKey(), ResultDefineManager.me().getFail().getLeft())
				.set(rdm.getMsgKey(), (msg != null && msg.length() > 0) ? msg : ResultDefineManager.me().getFail().getRight()); // >0是失败
	}
	protected Kv retFail(String code, String msg) {
		ResultDefineManager rdm = ResultDefineManager.me();
		return Kv.by(rdm.getCodeKey(), code).set(rdm.getMsgKey(), msg);
	}
	protected void setResponseHead(String key, String value) {
		getResponse().setHeader(key, value);
	}
	protected void addResponseHead(String key, String value) {
		getResponse().addHeader(key, value);
	}
	
	// 对ipan_poi报表组件输出结果进行简单过滤
	protected String filterXlsResultText(XlsResult xlsResult, String downloadFailBtnHtml) {
		StringBuilder buf = new StringBuilder();
		String text = xlsResult.getText().replace("\\", "/");
		String[] strs = text.split(Platforms.LINE_SEPARATOR);
		for (String s : strs) {
			if (s.startsWith("失败记录文件保存路径")) { // 导入文件失败，会保存错误记录文件名保存到session；
				String fileName = StringUtils.substringAfterLast(s, "/");
//				getSession().setAttribute(EXCEL_RECORD_CODE, fileName); // 放弃session，调用者自己控制！
																		// 一般每日半夜清一次，失败记录文件当日就让它一直放着；
																		// 大文件耗时长的导入，可特殊处理！
				buf.append("下载失败记录文件：").append(downloadFailBtnHtml);
//					.append("<a href=\"/downloadXlsError\" target=\"_blank\" class=\"layui-btn layui-btn-danger layui-btn-xs\"><i class=\"layui-icon\">&#xe601;</i>点击下载</a><br/>");
					//.append("<font color=\"red\" style=\"margin: 0px 0px 0px 20px\">（请右击选择目标另存为）</font>").append("<br/>");
			} else if (!s.startsWith("导入文件保存路径") && !s.startsWith("----") ) {
				buf.append(s).append("<br/>");
			}
		}
		return buf.toString();
	}
	
}


